import pickle
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import STL
import seaborn as sns
import statsmodels.api as sm
import numpy as np
with open('../../data/climate_data.pkl', 'rb') as f:
data = pickle.load(f)
cities = data['cities']
stations_info = data['stations_info']
city_list = list(cities.keys())
def decompose_all_cities(cities, city_list, column='TEMP', freq='ME', period=12, plot=True):
"""
Wykonuje dekompozycję STL dla wybranego parametru meteorologicznego (np. temperatury)
w danych miesięcznych dla podanych miast.
Parametry:
----------
cities : dict
Słownik z danymi meteorologicznymi, gdzie kluczem jest nazwa miasta, a wartością DataFrame.
city_list : list
Lista nazw miast do analizy.
column : str, opcjonalnie, domyślnie 'TEMP'
Nazwa kolumny z danymi do dekompozycji (np. temperatura).
freq : str, opcjonalnie, domyślnie 'ME' (miesięczne)
Częstotliwość próbkowania danych do resamplingu (np. 'ME' - koniec miesiąca).
period : int, opcjonalnie, domyślnie 12
Okres sezonowości (np. 12 miesięcy).
plot : bool, opcjonalnie, domyślnie True
Czy wyświetlić wykresy dekompozycji.
Zwraca:
--------
dict
Słownik z wyodrębnionymi trendami STL dla każdego miasta.
"""
trends = {}
for city in city_list:
df = cities[city].copy()
df['DATE'] = pd.to_datetime(df['DATE'])
# Grupowanie danych do miesięcznych wartości średnich dla wybranego parametru
monthly = df.set_index('DATE').resample(freq)[column].mean()
# Ustawienie pełnego, ciągłego indeksu miesięcznego, brakujące miesiące uzupełnione NaN
monthly = monthly.asfreq(freq)
# Usuwanie brakujących wartości NaN (STL nie radzi sobie z NaN, ale toleruje krótkie luki)
monthly_clean = monthly.dropna()
# Sprawdzenie czy jest wystarczająco danych do dekompozycji (minimum 2 pełne okresy)
if len(monthly_clean) < 2 * period:
print(f"Zbyt mało danych do dekompozycji dla miasta {city}")
continue
# Dekompozycja STL - rozdzielenie na trend, sezonowość i reszty
stl = STL(monthly_clean, period=period, robust=True)
res = stl.fit()
# Zapisanie wyodrębnionego trendu dla miasta
trends[city] = res.trend
if plot:
# Wizualizacja wyników dekompozycji
plt.figure(figsize=(12, 8))
plt.suptitle(f'Dekompozycja STL miesięczna dla {city} – {column}', fontsize=14)
plt.subplot(4, 1, 1)
plt.plot(monthly_clean.index, monthly_clean.values, label='Oryginalne')
plt.legend()
plt.subplot(4, 1, 2)
plt.plot(res.trend.index, res.trend.values, label='Trend', color='orange')
plt.legend()
plt.subplot(4, 1, 3)
plt.plot(res.seasonal.index, res.seasonal.values, label='Sezonowość', color='green')
plt.legend()
plt.subplot(4, 1, 4)
plt.plot(res.resid.index, res.resid.values, label='Reszty', color='red')
plt.legend()
plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()
return trends
# Dekopozycja dla średniej temperatury
trends_temp = decompose_all_cities(cities, city_list, 'TEMP')
# Dekopozycja dla temperatury minimalnej
trends_min = decompose_all_cities(cities, city_list, column='MIN')
# Dekopozycja dla temperatury maksymalnej
trends_max = decompose_all_cities(cities, city_list, 'MAX')
Analiza wyników dekompozycji STL¶
Wykonano dekompozycję STL dla wszystkich miast, osobno dla temperatury średniej, minimalnej i maksymalnej.
Ze względu na wysoką korelację między tymi zmiennymi, skupiono się na ogólnych wzorcach sezonowości oraz długoterminowych trendach.
Porównanie trendów między miastami znajduje się na kolejnych wykresach. Poniżej przedstawiono krótkie podsumowanie charakterystyki danych dla poszczególnych miast:
- Ateny – bardzo regularna sezonowość; trend jest zmienny, ale długoterminowo wykazuje wzrost.
- Berlin – długi okres interpolacji danych (ponad 10 lat), co utrudnia interpretację.
- Bruksela – stabilna sezonowość i wyraźnie rosnący trend w ciągu 40 lat.
- Budapeszt – stała sezonowość, wyraźny wzrost temperatury w trendzie.
- Lizbona – cykle sezonowe są stałe; trend niewielki, ale rosnący.
- Londyn – mniej regularna sezonowość i słabo rosnący trend.
- Madryt – dobrze widoczna sezonowość i rosnący trend temperatury.
- Moskwa – zakłócenia w sezonowości wpływają na trudność w ocenie trendu, który lokalnie maleje.
- Paryż – krótki okres interpolowanych danych, ale widoczna sezonowość i rosnący trend.
- Praga – stała sezonowość i wyraźny wzrost temperatury.
- Warszawa – dobra jakość danych, stabilna sezonowość i rosnący trend.
- Wiedeń – regularne cykle sezonowe, trend rosnący, choć powoli.
def plot_trends(trends_temp, trends_max, trends_min, city_list):
"""
Rysuje wykresy trendów temperatur dla wybranych miast na podstawie wyników dekompozycji STL.
Parametry:
----------
trends_temp : dict
Słownik trendów dla średniej temperatury (TEMP), gdzie kluczem jest nazwa miasta.
trends_max : dict
Słownik trendów dla maksymalnej temperatury (MAX).
trends_min : dict
Słownik trendów dla minimalnej temperatury (MIN).
city_list : list
Lista nazw miast do uwzględnienia na wykresach.
Działanie:
----------
Dla każdego zestawu trendów (TEMP, MAX, MIN) tworzone są osobne wykresy liniowe pokazujące zmiany trendu w czasie dla wybranych miast.
"""
palette = sns.color_palette("tab20", n_colors=len(city_list))
# Funkcja pomocnicza do rysowania pojedynczego wykresu trendów
def plot_single_trend(trends_dict, title):
plt.figure(figsize=(12, 6))
for i, city in enumerate(city_list):
if city in trends_dict:
trend = trends_dict[city]
plt.plot(trend.index, trend.values, label=city, color=palette[i])
plt.title(title)
plt.xlabel('Rok')
plt.ylabel('Trend')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# Rysowanie wykresów dla średniej, maksymalnej i minimalnej temperatury
plot_single_trend(trends_temp, 'Trend średniej temperatury (TEMP)')
plot_single_trend(trends_max, 'Trend maksymalnej temperatury (MAX)')
plot_single_trend(trends_min, 'Trend minimalnej temperatury (MIN)')
plot_trends(trends_temp, trends_max, trends_min, city_list)
Analiza trendu na wspólnym wykresie¶
Na wykresie wspólnym dla wszystkich miast wyraźnie widoczna jest tendencja wzrostowa temperatur w większości lokalizacji.
W szczególności:
- Miasta południowe takie jak Ateny, Madryt i Lizbona wykazują wyraźnie rosnące trendy, przy czym wzrost jest bardziej stromy niż w przypadku miast północnych.
- Miasta Europy Środkowej (Praga, Warszawa, Wiedeń, Budapeszt) również odnotowują systematyczny wzrost temperatur, choć nieco bardziej łagodny.
- Miasta zachodnioeuropejskie jak Bruksela, Paryż czy Londyn również prezentują wyraźny trend wzrostowy, ale z pewnymi okresami spłaszczenia, prawdopodobnie związanymi z jakością danych.
- Moskwa, jako jedno z najchłodniejszych miast w zestajako jedyne wykazuje trend malejący jednak cięzko to interpetować ze względu na dziwne zachowanie danych w latach 2015-2020 zmienne.
- Berlin i Paryż posiadają fragmenty danych interpolowanych, przez co interpretacja ich trendów powinna być ostrożna, jednak mimo to linie trendu również wskazują na wzrost temperatur.
Podsumowując, wspólny wykres ukazuje spójną i alarmującą tendencję wzrostu temperatur w większości analizowanych miast europejskich na przestrzeni ostatnich dekad. Wzrost ten jest zgodny z globalnymi obserwacjami dotyczącymi zmian klimatycznych.
def plot_weather_phenomena_trends(cities, city_list, phenomena):
"""
Analizuje i wizualizuje trendy miesięcznej liczby dni z określonymi zjawiskami pogodowymi
dla wybranych miast, stosując wygładzanie LOWESS.
Parametry:
----------
cities : dict
Słownik z danymi pogodowymi, gdzie kluczem jest nazwa miasta, a wartością DataFrame.
city_list : list
Lista nazw miast do analizy.
phenomena : list
Lista nazw zjawisk pogodowych (np. ['Rain', 'Snow', 'Hail', 'Thunder']).
Działanie:
----------
- Grupuje dane dzienne do miesięcznych sum dni, w których wystąpiło dane zjawisko.
- Stosuje wygładzanie LOWESS na szeregach czasowych, aby uwidocznić trendy.
- Pomija Londyn na wykresach dla zjawisk "Rain" i "Snow".
- Zwraca słownik z trendami LOWESS dla każdego miasta i zjawiska.
Zwraca:
--------
dict
Słownik z trendami LOWESS, struktura: {zjawisko: {miasto: Series trendu}}
"""
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import statsmodels.api as sm
palette = sns.color_palette("tab20", n_colors=len(city_list))
trends = {pheno: {} for pheno in phenomena}
for phenomenon in phenomena:
plt.figure(figsize=(12, 6))
for i, city in enumerate(city_list):
# Pomijamy Londyn tylko dla Rain i Snow
if city == 'Londyn' and phenomenon in ['Rain', 'Snow']:
continue
df = cities[city].copy()
df['DATE'] = pd.to_datetime(df['DATE'])
df['YearMonth'] = df['DATE'].dt.to_period('M')
# Grupowanie po miesiącach i sumowanie dni z wystąpieniem zjawiska
monthly_counts = df.groupby('YearMonth')[phenomenon].sum()
monthly_counts.index = monthly_counts.index.to_timestamp()
x = np.arange(len(monthly_counts))
y = monthly_counts.values
# Pomijanie miast z niewystarczającą ilością danych
if len(x) < 10:
continue
# Wygładzanie LOWESS
lowess_smoothed = sm.nonparametric.lowess(y, x, frac=0.2, return_sorted=False)
# Zapisywanie trendu do słownika
trend_series = pd.Series(lowess_smoothed, index=monthly_counts.index)
trends[phenomenon][city] = trend_series
# Rysowanie wykresu trendu
plt.plot(monthly_counts.index, lowess_smoothed, label=city, color=palette[i])
plt.title(f'Trend miesięcznej liczby dni z występowaniem: {phenomenon} (LOWESS)')
plt.xlabel('Rok')
plt.ylabel('Liczba dni w miesiącu')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
return trends
phenomena = ['Rain', 'Snow', 'Hail', 'Thunder']
trends_phenomena = plot_weather_phenomena_trends(cities, city_list, phenomena)
Analiza trendu zjawisk pogodowych¶
Opis postępowania¶
W celu analizy zmian w częstości występowania wybranych zjawisk pogodowych (Rain, Snow, Hail, Thunder) dokonano przekształcenia dziennych danych do postaci miesięcznych sum dni, w których dane zjawisko wystąpiło. Na ich podstawie zastosowano wygładzanie LOWESS (Locally Weighted Scatterplot Smoothing), które pozwala uchwycić ogólny trend czasowy z uwzględnieniem lokalnych wahań.
Wykresy zostały wygenerowane osobno dla każdego zjawiska i obejmują wszystkie analizowane miasta. Londyn został pominięty na wykresach dla zjawisk „Rain” i „Snow”, ponieważ jego wartości znacząco odbiegały od pozostałych i zniekształcały skalę wykresu.
Wyniki analizy trendów¶
🌧️ Deszcz (Rain)¶
Większość miast wykazuje stabilny trend liczby dni deszczowych w miesiącu — nie zaobserwowano wyraźnych zmian w czasie.
Wyjątki:
- Lizbona i Budapeszt wykazują zauważalny spadkowy trend, co może świadczyć o zmniejszającej się liczbie dni deszczowych w tych miastach.
❄️ Śnieg (Snow)¶
Dla większości miast trend jest płaski, co oznacza brak istotnych zmian w liczbie dni ze śniegiem.
Wyjątek:
- Moskwa wykazuje wyraźny trend spadkowy, co może wskazywać na coraz rzadsze występowanie dni śnieżnych.
🌨️ Grad (Hail)¶
W przypadku gradu trudno mówić o wyraźnych trendach — dane dla wielu miast zawierają liczne zera lub bardzo niskie wartości, przez co większość trendów jest bliska zeru lub mało wiarygodna.
⛈️ Burze (Thunder)¶
- W przeważającej liczbie miast widoczny jest spadkowy trend liczby dni burzowych.
- Ateny i Wiedeń są wyjątkami, gdzie trend ten jest stabilny lub delikatnie rosnący.
Podsumowanie¶
Z analizy wynika, że:
- Trendy dla deszczu i śniegu są w większości stabilne, z lokalnymi wyjątkami.
- Grad występuje zbyt rzadko, aby wyciągać jednoznaczne wnioski.
- Burze wykazują najbardziej spójny trend spadkowy w wielu miastach, co może być efektem zmieniającej się charakterystyki klimatycznej.
# Utworznie struktury z trendami
all_trends = {
'temperature': {
'TEMP': trends_temp,
'MAX': trends_max,
'MIN': trends_min
},
'phenomena': trends_phenomena
}
# Zapisanie do pliku
with open('../../data/all_trends.pkl', 'wb') as f:
pickle.dump(all_trends, f)